using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Me.Prettyprint.Cassandra.Connection;
using Me.Prettyprint.Cassandra.Connection.Factory;
using Me.Prettyprint.Hector.Api;
using Me.Prettyprint.Hector.Api.Factory;

namespace Me.Prettyprint.Cassandra.Service
{
    /// <summary>
    /// CassandraHostConfigurator is used to load the configuration settings
    /// and setup the connection. The host settings should be in the format
    /// host:port.
    /// </summary>
	public sealed class CassandraHostConfigurator
	{
        public static readonly ClockResolution DEF_CLOCK_RESOLUTION = HFactory.CreateClockResolution(ClockResolutionFields.MICROSECONDS_SYNC);
        private static ClockResolution clockResolution = DEF_CLOCK_RESOLUTION;

        private Type clientFactoryClass = Type.GetType("Me.Prettyprint.Cassandra.Connection.Factory.HThriftClientFactoryImpl");

        public CassandraHostConfigurator()
        {
            InitBlock();
            Hosts = null;
        }

        /// <summary> Creates a new {@code CassandraHostConfigurator} from the specified hosts String, formatted as
        /// {@code host[:port][,host[:port]...]}.
        /// </summary>
        /// <param name="hosts">The hosts to create {@link CassandraHost}s from.
        /// </param>
        public CassandraHostConfigurator(string hosts) : this()
        {
            Hosts = hosts;
        }

        private void InitBlock()
		{
            OpTimer = new NullOpTimer();
			Port = CassandraHost.DEFAULT_PORT;
			MaxActive = CassandraHost.DEFAULT_MAX_ACTIVE;
            ClientFactoryType = typeof(HThriftClientFactoryImpl);
			Lifo = CassandraHost.DEFAULT_LIFO;
			MaxWaitTimeWhenExhausted = CassandraHost.DEFAULT_MAX_WAITTIME_WHEN_EXHAUSTED;
			UseThriftFramedTransport = CassandraHost.DEFAULT_USE_FRAMED_THRIFT_TRANSPORT;
            UseHostTimeoutTracker = false;
            UseSocketKeepalive = false;
            RunAutoDiscoveryAtStartup = true;
			MaxFrameSize = CassandraHost.DEFAULT_MAX_FRAME_SIZE;
            RetryDownedHosts = true;
			RetryDownedHostsQueueSize = CassandraHostRetryService.DEF_QUEUE_SIZE;
			RetryDownedHostsDelayInSeconds = CassandraHostRetryService.DEF_RETRY_DELAY;
            AutoDiscoverHosts = true;
			AutoDiscoveryDelayInSeconds = NodeAutoDiscoverService.DEF_AUTO_DISCOVERY_DELAY;
            LoadBalancingPolicy = new RoundRobinBalancingPolicy();
			HostTimeoutCounter = HostTimeoutTracker.DEF_TIMEOUT_COUNTER;
			HostTimeoutWindow = HostTimeoutTracker.DEF_TIMEOUT_WINDOW;
			HostTimeoutSuspensionDurationInSeconds = HostTimeoutTracker.DEF_NODE_SUSPENSION_DURATION_IN_SECONDS;
			HostTimeoutUnsuspendCheckDelay = HostTimeoutTracker.DEF_NODE_UNSUSPEND_CHECK_DELAY_IN_SECONDS;
			MaxConnectTimeMillis = CassandraHost.DEFAULT_MAX_CONNECT_TIME;
			MaxLastSuccessTimeMillis = CassandraHost.DEFAULT_MAX_LAST_SUCCESS_TIME;
            if (LogLevel == null)
            {
                LogLevel = "Error";
            }
		}
        public string Hosts
        {
            get;
            set;
        }
        public int MaxActive
        {
            get;
            set;
        }
        public long MaxWaitTimeWhenExhausted
        {
            get;
            set;
        }
        public int CassandraThriftSocketTimeout
        {
            get;
            set;
        }
		public bool RetryDownedHosts
		{
            get;
            set;
		}
        public int RetryDownedHostsQueueSize
        {
            get;
            set;
        }
        public int RetryDownedHostsDelayInSeconds
        {
            get;
            set;
        }
		public HOpTimer OpTimer
		{
			get;
			set;
		}
		public bool Lifo
		{
			get;
			set;
		}
		public int Port
		{
			get;
			set;
		}
        public bool UseThriftFramedTransport
        {
            get;
            set;
        }
        public int MaxFrameSize
        {
            get;
            set;
        }
		public bool AutoDiscoverHosts
		{
			get;
			set;
		}
		public int AutoDiscoveryDelayInSeconds
		{
			get;
			set;
		}
        public List<string> AutoDiscoveryDataCenters
        {
            get;
            set;
        }
        public string LogLevel
        {
            get;
            set;
        }
		public void SetAutoDiscoveryDataCenter(string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				AutoDiscoveryDataCenters = new List<string>(value.Split());
			}
		}
		public LoadBalancingPolicy LoadBalancingPolicy
		{
			get;
			set;
		}
		public int HostTimeoutCounter
		{
			get;
			set;
		}
		public int HostTimeoutWindow
		{
			get;
			set;
		}
		public int HostTimeoutSuspensionDurationInSeconds
		{
			get;
			set;
		}
		public int HostTimeoutUnsuspendCheckDelay
		{
			get;
			set;
		}
		public bool UseHostTimeoutTracker
		{
			get;
			set;
		}
		public bool RunAutoDiscoveryAtStartup
		{
			get;
			set;
		}
		public bool UseSocketKeepalive
		{
			get;
			set;
		}
		public string ClientFactoryClass
		{
			set
			{
				string className = value.Contains(".")?value:"Me.Prettyprint.Cassandra.Connection.Factory." + value;
				Type temp;
				try
				{
					temp = System.Type.GetType(className);
                    if (temp is HClientFactory)
                    {
                        ClientFactoryType = temp;
                    }
				}
				catch (System.Exception e)
				{
					throw e;
				}
			}
			
		}
        public long MaxConnectTimeMillis
		{
			get;
			set;
		}
        public long MaxLastSuccessTimeMillis
		{
			get;
			set;
		}
		public CassandraHost[] buildCassandraHosts()
		{
            if (Hosts == null)
            {
                throw new System.ArgumentException("Need to define at least one host in order to apply configuration.");
            }
            else
            {
                System.String[] hostVals = Hosts.Split(',');
                CassandraHost[] cassandraHosts = new CassandraHost[hostVals.Length];
                for (int x = 0; x < hostVals.Length; x++)
                {
                    CassandraHost cassandraHost = Port == CassandraHost.DEFAULT_PORT ? new CassandraHost(hostVals[x].Trim()) : new CassandraHost(hostVals[x], Port);
                    ApplyConfig(cassandraHost);
                    cassandraHosts[x] = cassandraHost;
                }
                return cassandraHosts;
            }
		}
		public void ApplyConfig(CassandraHost cassandraHost)
		{
			cassandraHost.MaxActive = MaxActive;
			cassandraHost.Lifo = Lifo;
			cassandraHost.MaxWaitTimeWhenExhausted = MaxWaitTimeWhenExhausted;
			cassandraHost.UseThriftFramedTransport = UseThriftFramedTransport;
			cassandraHost.MaxFrameSize = MaxFrameSize;
			cassandraHost.UseSocketKeepalive = UseSocketKeepalive;
			cassandraHost.MaxConnectTimeMillis = MaxConnectTimeMillis;
			cassandraHost.MaxLastSuccessTimeMillis = MaxLastSuccessTimeMillis;
			
			// this is special as it can be passed in as a system property
			if (CassandraThriftSocketTimeout > 0)
			{
				cassandraHost.CassandraThriftSocketTimeout = CassandraThriftSocketTimeout;
			}
		}
		public static void SetClockResolution(System.String resolutionString)
		{
			clockResolution = HFactory.CreateClockResolution(resolutionString);
		}
		public override System.String ToString()
		{
			StringBuilder s = new StringBuilder();
			s.Append("CassandraHostConfigurator<");
			s.Append("clockResolution=");
			s.Append(clockResolution);
			s.Append("&cassandraThriftSocketTimeout=");
			s.Append(CassandraThriftSocketTimeout);
			s.Append("&maxWaitTimeWhenExhausted=");
			s.Append(MaxWaitTimeWhenExhausted);
			s.Append("&maxActive=");
			s.Append(MaxActive);
			s.Append("&hosts=");
			s.Append(Hosts);
			s.Append("&useThriftFramedTransport=");
			s.Append(UseThriftFramedTransport);
			s.Append("&maxFrameSize=");
			s.Append(MaxFrameSize);
			s.Append("&retryDownedHosts=");
			s.Append(RetryDownedHosts);
			s.Append("&opTimer=");
			s.Append(OpTimer);
			s.Append(">");
			return s.ToString();
		}
		public static ClockResolution GetClockResolution()
		{
			return CassandraHostConfigurator.clockResolution;
		}
		public static void  SetClockResolution(ClockResolution clockResolution)
		{
			CassandraHostConfigurator.clockResolution = clockResolution;
		}
        public Type ClientFactoryType
        {
            get;
            set;
        }
		static CassandraHostConfigurator()
		{
            DEF_CLOCK_RESOLUTION = HFactory.CreateClockResolution(Me.Prettyprint.Hector.Api.ClockResolutionFields.MICROSECONDS_SYNC);
		}
	}
}